// Ref: http://opencv-code.com/Using_the_Fast_Fourier_Transform_Library_FFTW#computing-real-1d-dft -- no longer available
// Ref: http://ubuntuforums.org/showthread.php?t=1489837&page=2

#include "AudioCaptureWidget.h"

#define _USE_MATH_DEFINES

#define DEBUG 1
#define DEBUG2 0

AudioCaptureWidget::AudioCaptureWidget(QWidget *parent)  {


    // widget color
    this->setPalette((QColor(255,255,255,255)));


    // intializing peak freqency array
    peakFrequencyArray = new double[75000];
    loopCountArray = new double[75000];




    qDebug()<< sizeof(peakFrequencyArray);
    for(int i=0;i<75000; i++) {
        peakFrequencyArray[i] = 0.0;
        loopCountArray[i] = i;
   }

    // symbol for peak in fft
    peakSymbol = new QwtSymbol();
    peakSymbol->setStyle(QwtSymbol::Ellipse);
    peakSymbol->setSize(7,7);

    // set loop time
    notifyInterval = 10;

    // initialize no. of loops executed
    loopCount=0;

    /*************Audio Setup***************/
    audioBuffer = new QBuffer();

    // set frequency
    freq = 44100;

    // set format for audio recording
    format.setFrequency(freq);
    format.setChannels(1);
    format.setSampleSize(16);
    format.setSampleType(QAudioFormat::SignedInt);
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setCodec("audio/pcm");

    // initialize audio device info and check if audio format is supported
    QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice());
    if (!info.isFormatSupported(format)) {
        qWarning() << "Default format not supported - trying to use nearest";
        format = info.nearestFormat(format);
    }
    qDebug()<<"Format: Frequency = "<<format.frequency()<<endl
           <<"Channels =  "<<format.channels()<< endl
          <<"Sample Size = "<<format.sampleSize()<< endl
         <<"Sample Type = " << format.sampleType() << endl
        <<"ByteOrder =  "<<format.byteOrder();

    // create new QAudioInput object
    audio = new QAudioInput(info,format, this);
    //audio->setBufferSize(1764);

    // initialize timer
    audioTimer = new QTimer();

//    audio->setNotifyInterval(10);
    connect(audioBuffer, SIGNAL(readyRead()),this, SLOT(dataReady()));

    // state print function
    connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(statePrint(QAudio::State)));
    //connect(audioBuffer, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));

    //connect(audioTimer,SIGNAL(timeout()),this, SLOT(notified()));
    audioTimer->setInterval(notifyInterval);

    //connect(audio, SIGNAL(notify()), this, SLOT(dataReady()));
    //connect(audioBuffer, SIGNAL(bytesWritten(qint64)), this , SLOT(bytesWritten(qint64)));

    /********** Plot Setup *************/
    // setting a default high threshold initially
    HLThreshold = 20000000;
    LLThreshold = 0;
    stepCountLimit = 50000;
    peakFreqLimit = 2000;
    peakFreqLimitInd = 200;
    band = 1000;

    // threshold line
    xLine[0] = 2000;
    xLine[1] = 3000;
    yLine[0] = 5000;
    yLine[1] = 5000;

    // create qwtplot object
    fftPlot = new QwtPlot();
    audioSignalPlot = new QwtPlot();

    // creat curve object to plot fft
    fftCurve = new QwtPlotCurve();
    audioSignalCurve = new QwtPlotCurve();
    thresholdLine = new QwtPlotCurve();

    audioSignalCurve->attach(audioSignalPlot);

    // attach curve to plot
    fftCurve->attach(fftPlot);
    thresholdLine->attach(fftPlot);

    int linePts = 2;

    //thresholdLine->setData(xLine, yLine, linePts);
    //thresholdLine->setSamples(xLine, yLine, linePts);
    //thresholdLine->setData(QwtSeriesData(xLine), QwtSeriesData(yLine), linePts);


    // set axis for plot

    //plot->setAxisScale(0,0,(HLThreshold*2),(HLThreshold*2)/5);
    //fftPlot->setAxisScale(2,1500,3000,500);
    QFont serifFont("Times", 10);
    QFont titleFont("Times",14);
    fftPlot->setAxisFont(0, serifFont);
    fftPlot->setAxisFont(2, serifFont);

    QwtText axis1("Power");
    axis1.setFont(titleFont);
    fftPlot->setAxisTitle(0,axis1);

    QwtText axis2("F [Hz]");
    axis2.setFont(titleFont);
    fftPlot->setAxisTitle(2,axis2);


    QwtText title("FFT of audio input");
    title.setFont(titleFont);
    fftPlot->setTitle(title);

    // setting color for plot canvas
    fftPlot->setCanvasBackground(QColor(255,255,255,255));
    audioSignalPlot->setCanvasBackground(QColor(255,255,255,255));
    fftPlot->setFrameStyle(1);
    audioSignalPlot->setFrameStyle(1);


    /********layout*****************/
    // widget layout and setup
    QGridLayout *layout = new QGridLayout();
    layout->addWidget(fftPlot,0,0,1,1,0);
    layout->addWidget(audioSignalPlot,0,1,1,1,0);
    this->setLayout(layout);
    this->setFixedSize(1280,480);
    this->setWindowTitle("FFT and raw signal plots");
    QRect rec ;
    rec= QApplication::desktop()->geometry();
    //this->move(QPoint(100,100));4

    // old data
    for (int i = 1; i<2000; i++) {
        dataOld[i]=0;
        spectrumOld[i] = 0;
        dataDiff[i] =0;
    }


    //connect(audioBuffer, SIGNAL(readyRead()), this, SLOT(saveNoise()));

    //recordNoise();


}


AudioCaptureWidget::~AudioCaptureWidget()
{
    //audioTimer->stop();
    //audio->stop();
    //outputFile.close();
}

/************************************************************************************************************
Function Name: notified()

funtion: This function reads the audio data for every m no. of loops executed and perfoms fft on that data.
          It also plots the fft and emits a singal fftDone() so that the main program knows that the fft
          analysis has been done and it can proceed with sending commands to the manipulator.

************************************************************************************************************/

void AudioCaptureWidget::dataReady()
{

    //http://www.qtforum.org/article/32267/qaudioinput-into-qbytearry-ain-t-that-suppose-to-work.html
    audioBuffer->seek(0);

    // 4
    totalSize = audioBuffer->size();

    // qDebug()<< "totalSize"<< totalSize << loopCount;

    len = totalSize/4;
    //if(loopCount%1==0) {
    if(len>5200/4 && len<10000/4) {
        rawAudio = new int[len];

        // qDebug()<< "totalSize"<< totalSize << " Bytes Ready"<< audioBuffer->bytesAvailable()<<"Loop no."<<loopCount << " Period Size "<< audio->periodSize();

        for(int k=0;k<len;k++)
        {
            rawAudio[k] = qFromLittleEndian<qint16>((uchar*)audioBuffer->data().mid(k*4, 4).data());
            int range  = 50;

//            if(dataArray[k] > range)
//                dataArray[k] = dataArray[k] - range;

//            if(dataArray[k] < -range)
//                dataArray[k] = dataArray[k] + range;

//            if(dataArray[k] > -range && dataArray[k] < range)
//                dataArray[k]= 0;

            // qDebug()<< "Data at "<<k<<" = "<<dataArray[k]<< " in loop = "<<loopCount;
        }

        audioBuffer->buffer().clear();

        rawAudioD = new double[len];
        xAxis = new double[len];

        int scale = 44100/(2*len);

        for(int i=0; i <len; i++)
        {
            xAxis[i] = i*scale;
            rawAudioD[i] = (double)(rawAudio[i]);
            //qDebug()<<dataArrayd[i];
        }



        double *in;
        in = (double *) fftw_malloc(sizeof(double) * len);

        // fftw_complex out[len];

        //double out[len];
        double *out;
        out = (double *) fftw_malloc(sizeof(double) * len);
        double spectrum[len];
        double spectrumDB[len];

        for(int h = 0 ; h<len; h++)
        {
             //dataArrayd[h] = lowPassFilter(dataArrayd[h], dataOld[h], notifyInterval * 0.001, 0.005);

            in[h] = rawAudioD[h] * 0.5 * (1 - cos(2 * M_PI * h / (len - 1)));
//           dataOld[h]= rawAudioD[h];
        }

        //audioSignalCurve->setSamples(x,dataArrayd,len);
        //audioSignalPlot->replot();



        //dataOld = data;

        // performing fft on data
        //p = rfftw_create_plan(n, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE);
        fftw_plan p;
        p = fftw_plan_r2r_1d(len, in, out, FFTW_R2HC, FFTW_ESTIMATE);
        //rfftw_one(p,in,out);
        fftw_execute(p);

#if DEBUG



        spectrum[0]=out[0]*out[0] ;
        for(int m = 1 ; m < (len+1)/2; ++m) {
            spectrum[m]= (out[m]*out[m] + out[len + 1 -m]*out[len + 1 -m]) ;

            //            dataDiff[m] = abs(spectrum[m] - spectrumOld[m]);
            //            spectrumOld[m] = spectrum[m];


            spectrumDB[m]=10*log10((out[m]*out[m] + out[len + 1-m]*out[len + 1- m]));
        }


        // finding peak value between limits
        maxAmp = 0;
        peakFreqLimitInd = (int)(peakFreqLimit/scale);
        for(int m = (peakFreqLimitInd-((int)band/scale)) ; m < (peakFreqLimitInd+(int)band/scale); ++m) {
            if(spectrum[m]>maxAmp)
            {
                maxAmp=spectrum[m];
                maxInd = m;
                peakFrequencyArray[loopCount] = (m * scale);
                loopCountArray[loopCount] = loopCount;
            }
        }

      //  if(((maxAmp>HLThreshold) | (maxAmp<LLThreshold)) & (actualStepCount>stepCountLimit) )
        if(((maxInd*scale < 2300  && maxInd*scale > 2000) ||  (maxAmp>HLThreshold)) && (actualStepCount>stepCountLimit) )
        {
            complete = complete + 1;
            qDebug()<< "End of travel detected at: "<< endl
                           <<"Frequency = "<<maxInd*scale<< endl <<"with amplitude MaxAmp = "<<maxAmp <<endl
                           << spectrum[maxInd]<<xAxis[maxInd]<<" "<< HLThreshold <<" " << LLThreshold;

            if (complete==1) {
                emit trialComplete();
                // audioBuffer->reset();

            }

        }

            // drawing marker for peak point
        marker.setYValue(spectrum[maxInd]);
        marker.setXValue(xAxis[maxInd]);

        marker.setSymbol(peakSymbol);
        marker.attach(fftPlot);

        // finding peak value between limits


        fftCurve->setSamples(xAxis, spectrum, len/2);
        fftPlot->replot();

        //audioSignalCurve->setSamples(xAxis, spectrumDB, len/2);
        audioSignalCurve->setSamples(loopCountArray, peakFrequencyArray, loopCount);
        audioSignalPlot->setAxisScale(1, 0, loopCount, loopCount/10);
        audioSignalPlot->replot();

#endif

        //qDebug()<<"Loop count "<<loopCount;

        fftw_destroy_plan(p);




        //delete[] rawAudioD;
        delete[] in;
        delete[] out;

            loopCount = loopCount+1;
    }



    // emitting fftDone signal to indicate fft anlysis is complete
    emit fftDone();

}



void AudioCaptureWidget::stop()
{
    audio->suspend();

    //fclose(pFile);
    // outputFile.close();
    qDebug()<< "STOP";
    audioTimer->stop();
    audioBuffer->buffer().clear();
    audioBuffer->close();

}

void AudioCaptureWidget::start()
{
    complete = 0;
    audioBuffer->buffer().clear();

    audioBuffer->open(QIODevice::ReadWrite);

    audio->start(audioBuffer);
    audioTimer->start();

    // qDebug()<< "Buffer Size = "<< audio->bufferSize();
}

void AudioCaptureWidget::statePrint(QAudio::State state)
{
    // qDebug()<< "State = "<<state;
}

void AudioCaptureWidget::bytesWritten(qint64 written)
{
    // qDebug()<< "Bytes written = "<<written;
}

